<template>
  <div v-loading="loading">
    <el-tabs
      v-model="currentClass"
      @tab-change="fitSize"
      tab-position="left"
      :style="`height: ${tableHeight}px`"
    >
      <el-tab-pane
        :style="`height: ${tableHeight}px`"
        v-for="(cls, idx) in allDseClass"
        :key="cls.Pk"
        :name="cls.Pk"
        :label="cls.ClassName"
      >
        <FullCalendar
          :ref="setCalendarRef"
          v-if="currentClass === cls.Pk"
          :options="calendarOpts"
        />
      </el-tab-pane>
    </el-tabs>
    <el-dialog v-model="showDialog" width="400">
      <el-form :model="currentSchedule" label-width="auto">
        <el-form-item label="课程">
          <el-select filterable v-model="currentSchedule.Course" @change="courseChanged">
            <el-option
              v-for="item in allCourse"
              :key="item.Pk"
              :value="item.Pk"
              :label="item.CourseName"
            >
              <div style="display: flex; align-items: center">
                <span style="flex: 1"
                  >{{ item.CourseName }} [{{ (item.Teacher as kvModel)._v }}]</span
                >
                <span
                  :style="`
                  display: inline-block;
                  width: 16px;
                  height: 16px;
                  background-color: ${item.Color};
                  border: 1px solid #ddd;`"
                ></span>
              </div>
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="教师">
          <el-select filterable v-model="currentSchedule.Teacher">
            <el-option
              v-for="item in allUser"
              :key="item.Pk"
              :value="item.Pk"
              :label="item.UserName"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="日期">
          {{ currentSchedule?.Date }}
        </el-form-item>
        <el-form-item label="时间">
          {{ currentSchedule?.Start }}-{{ currentSchedule?.End }}
        </el-form-item>
        <el-form-item label="备注">
          <el-input v-model="currentSchedule.Note"></el-input>
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button
            :disabled="!currentSchedule.Pk"
            :loading="subLoading"
            type="danger"
            @click="delSch"
            >删除</el-button
          >
          <el-button :loading="subLoading" type="primary" @click="commit"> 确定 </el-button>
        </div>
      </template>
    </el-dialog>
  </div>
  <div v-loading="loading" class="my_opt_btn_pannel">
    <div style="text-align: left"></div>
    <div style="text-align: right; padding-right: 20px">
      <el-date-picker :disabled-date="dateDisable" style="margin-right: 8px" v-model="date2Del" />
      <el-button :disabled="!date2Del" type="danger" @click="delByDate"> 批量删除 </el-button>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { listDseClass } from '@/api/dseclass'
import type { DseClassModel } from '@/type/DseClass'
import { listTimeTable } from '@/api/timetable'
import { useWindowSize } from '@vueuse/core'
import FullCalendar from '@fullcalendar/vue3'
import timeGridPlugin from '@fullcalendar/timegrid'
import interactionPlugin from '@fullcalendar/interaction'
import zhCn from '@fullcalendar/core/locales/zh-cn'
import type { CalendarOptions, EventInput } from '@fullcalendar/core'
import { useUserStore } from '@/stores/user'
import { ElMessage, ElMessageBox } from 'element-plus'
import { dateFormat, genRandomStr, timeFormat } from '@/utils/stringUtils'
import { DeconstructionKvModel, extractKvKey } from '@/utils/objUtils'
import { removeByClass, delSchedule, exchange, listByClass, saveSchedule } from '@/api/schedule'
import type { kvModel } from '@/type/ResponseData'
import type { TimeTableModel } from '@/type/TimeTable'
import type { ScheduleModel } from '@/type/Schedule'
import type { UserModel } from '@/type/User'
import { listUser } from '@/api/user'
import type { CourseModel } from '@/type/Course'
import { listCourse } from '@/api/course'
import router from '@/router'
const date2Del = ref<Date>()
const optUser = useUserStore()
if (!optUser.isAdm) router.push('/401')
const allCourse = ref<CourseModel[]>([])
const showDialog = ref(false)
const subLoading = ref(false)
const currentSchedule = ref<ScheduleModel>({})
const currentClass = ref('')
const winSize = useWindowSize()
const tableHeight = computed(() => winSize.height.value - 60 - 16 - 32 - 4) //60是MENU,16是PADDING,32是按钮区域，4是内容区和按钮区的距离
const loading = ref(false)
const allDseClass = ref<DseClassModel[]>([])
const allUser = ref<UserModel[]>([])
const calendarRef = ref<any>()
const start = ref<string>()
const end = ref<string>()
const tempIdLength = 10
const gotoDate = ref<string>()
let timetable: TimeTableModel[] = []
let schedules: ScheduleModel[] = []
const calendarOpts = ref<CalendarOptions>({
  initialView: 'timeGridWeek',
  plugins: [timeGridPlugin, interactionPlugin],
  locale: zhCn,
  height: '100%',
  dayMaxEvents: 11,
  allDaySlot: false,
  slotMinTime: '07:40',
  slotMaxTime: '20:10',
  slotDuration: '00:20:00',
  slotLabelInterval: '01:00:00',
  eventOverlap: true,
  editable: true,
  fixedWeekCount: false,
  datesSet: async (info) => {
    //如果start没有指定，说明是第一次进入，则正常执行
    //如果start已经指定，但是gotoDate尚未制定，则说明是切换过页签(fitSize调用过)，则先不获取数据，把控制权让给tab-change方法
    if (!gotoDate.value && start.value) return
    start.value = dateFormat(info.start)
    const da = info.end
    end.value = dateFormat(new Date(da.setDate(da.getDate() - 1)))
    await getSchedule()
  },
  eventClick: async (info) => {
    const s = schedules.find((r) => r.Pk === info.event.id)
    const seq = parseInt(info.event.id.split('#')[1]!)
    const copy: ScheduleModel = s
      ? (JSON.parse(JSON.stringify(s)) as ScheduleModel)
      : {
          Date: dateFormat(info.event.start!),
          Start: timeFormat(info.event.start!),
          End: timeFormat(info.event.end!),
          Seq: seq,
          DseClass: currentClass.value,
        }
    DeconstructionKvModel(copy)
    currentSchedule.value = copy
    showDialog.value = true
  },
  eventDrop: async (info) => {
    if (info.event.id.split('#')[0]!.length === tempIdLength) {
      ElMessage.error('无法拖动待定安排')
      info.revert()
      return
    }
    const schedulable = (calendarOpts.value.events as EventInput[]).find((r) => {
      const _dtStart = new Date(r.start as string)
      const _dtEnd = new Date(r.end as string)
      const myStart = info.event.start!
      return _dtStart <= myStart && _dtEnd >= myStart
    })
    if (!schedulable) {
      //这个时间段必须是可以安排课程的
      info.revert()
      return
    }
    const wantSchedule = JSON.parse(
      JSON.stringify(schedules.find((r) => r.Pk === info.event.id)),
    ) as ScheduleModel
    wantSchedule.Note = `调整自${wantSchedule.Date}第${wantSchedule.Seq}节课`
    wantSchedule.Date = dateFormat(info.event.start!)
    wantSchedule.Start = timeFormat(info.event.start!)
    wantSchedule.End = timeFormat(info.event.end!)

    if (schedulable.id!.split('#')[0]!.length === tempIdLength) {
      try {
        loading.value = true
        wantSchedule.Seq = parseInt(schedulable.id!.split('#')[1]!)
        await saveSchedule(wantSchedule)
      } catch {
        info.revert()
        return
      } finally {
        loading.value = false
      }
    } else {
      const otherSchedule = schedules.find((r) => r.Pk === schedulable.id)!
      let meger = false
      if (wantSchedule.Optional === 'Y' && otherSchedule.Optional === 'Y') {
        try {
          await ElMessageBox.confirm(
            '你正在调整的两节课都属于选修课，你希望换课还是合并？',
            '提示',
            {
              confirmButtonText: '换课',
              cancelButtonText: '合并',
              type: 'info',
            },
          )
        } catch {
          meger = true
        }
      }
      try {
        loading.value = true
        if (meger) {
          wantSchedule.Seq = otherSchedule.Seq
          await saveSchedule(wantSchedule)
        } else await exchange(wantSchedule.Pk!, otherSchedule.Pk!)
      } catch {
        info.revert()
        return
      } finally {
        loading.value = false
      }
    }
    await getSchedule()
    ElMessage.success('操作成功')
  },
})
const dateDisable = (d: Date) => {
  if (!start.value || !end.value) return true
  const date = new Date(dateFormat(d))
  return (
    new Date(start.value).getTime() > date.getTime() ||
    new Date(end.value).getTime() < date.getTime()
  )
}
const setCalendarRef = (el: any) => {
  if (el) {
    calendarRef.value = el
  }
}
onMounted(async () => {
  try {
    loading.value = true
    timetable = (await listTimeTable({ pageSize: 1000 })).Data
    allDseClass.value = (await listDseClass({ pageSize: 10000 })).Data
    allUser.value = (await listUser({ pageSize: 10000 })).Data
    allCourse.value = (await listCourse({ pageSize: 10000 })).Data
    currentClass.value = allDseClass.value[0]?.Pk!
    fitSize()
  } finally {
    loading.value = false
  }
})
const getSchedule = async () => {
  loading.value = true
  try {
    calendarOpts.value.events = []
    const data = (await listByClass(currentClass.value, start.value!, end.value!)).Data
    data.sort((a, b) => {
      return b.Seq! - a.Seq!
    })

    schedules = data
    const currentDate = new Date(start.value!)
    const endDate = new Date(end.value!)
    while (currentDate <= endDate) {
      const currentDateStr = dateFormat(currentDate)
      for (let i = 1; i <= 11; i++) {
        const d = data.filter((r) => r.Date === currentDateStr)
        const items = data.filter((r) => r.Seq === i && r.Date === currentDateStr)
        const specific = timetable.find((r) => r.Weekday === d[0]?.RefWeekDay && r.Seq === i)
        const t = specific || timetable.find((r) => r.Weekday === -1 && r.Seq === i)
        const event: EventInput = {
          id: `${genRandomStr(tempIdLength)}#${i}`,
          title: '待定',
          start: currentDateStr + ' ' + t!.Start,
          end: currentDateStr + ' ' + t!.End,
          color: '#DCDCDC',
        }
        const events = calendarOpts.value.events
        if (items.length === 0) {
          events.push(event)
        }

        items.forEach((item) => {
          const eCopy = JSON.parse(JSON.stringify(event)) as EventInput
          eCopy.id = item.Pk!
          eCopy.title = `${(item.Course as kvModel)._v}(${(item.Teacher as kvModel)._v})`
          if (item.Optional === 'N') {
            eCopy.color = item.Color
          } else {
            eCopy.color = '#fff'
            eCopy.textColor = item.Color!
            eCopy.borderColor = item.Color!
          }
          events.push(eCopy)
        })
      }
      currentDate.setDate(currentDate.getDate() + 1)
    }
  } finally {
    loading.value = false
  }
}
const fitSize = async () => {
  //先重置gotoDate,让datesSet方法直接退出
  if (gotoDate.value) gotoDate.value = undefined
  await nextTick()
  if (!calendarRef.value) return
  const api = calendarRef.value?.getApi()
  //如果dateSet方法直接退出（让出控制权），则重新定位到上一次指定的日期
  //此时因为为gotoDate提供了值，所以dateSet方法不会再让出控制权了
  gotoDate.value = start.value
  if (start.value) api.gotoDate(start.value)
  api.updateSize()
}
const courseChanged = (value: any) => {
  const currentCourse = allCourse.value.find((r) => r.Pk === value)
  currentSchedule.value.Color = currentCourse?.Color
  currentSchedule.value.Teacher = extractKvKey(currentCourse?.Teacher)
}
const commit = async () => {
  if (!currentSchedule.value.Course || !currentSchedule.value.Teacher) {
    ElMessage.error('请先确定课程和教师')
    return
  }
  subLoading.value = true
  try {
    if (!currentSchedule.value.Note) currentSchedule.value.Note = '计划外安排'
    await saveSchedule(currentSchedule.value)
    getSchedule()
    showDialog.value = false
  } finally {
    subLoading.value = false
  }
}
const delSch = async () => {
  subLoading.value = true
  try {
    await ElMessageBox.confirm('你确认需要删除这笔记录?', '提示', {
      confirmButtonText: '删除',
      cancelButtonText: '取消',
      type: 'warning',
    })
    await delSchedule(currentSchedule.value.Pk!)
    getSchedule()
    showDialog.value = false
  } finally {
    subLoading.value = false
  }
}
const filterEventsByDate = (dateToHide: string) => {
  const api = calendarRef.value?.getApi()
  api.getEvents().forEach((event: any) => {
    const eventDate = event.startStr.split('T')[0]
    if (eventDate === dateToHide) {
      event.setProp('display', 'none')
    }
  })
}
const showAllEvents = (dateToShow: string) => {
  const api = calendarRef.value?.getApi()
  api.getEvents().forEach((event: any) => {
    const eventDate = event.startStr.split('T')[0]
    if (eventDate === dateToShow) {
      event.setProp('display', 'auto')
    }
  })
}
const delByDate = async () => {
  const date = dateFormat(date2Del.value!)
  try {
    filterEventsByDate(date)
    await ElMessageBox.confirm(`你确认需要删除${date}这天的记录?`, '提示', {
      confirmButtonText: '删除',
      cancelButtonText: '取消',
      type: 'warning',
    })
    loading.value = true
    await removeByClass(currentClass.value, date)
    getSchedule()
    ElMessage.success('操作成功')
  } finally {
    showAllEvents(date)
    loading.value = false
  }
}
</script>
<style scoped>
:deep(.fc .fc-toolbar) {
  padding: 2px 2px !important;
  min-height: 24px !important;
  margin-bottom: 2px !important;
}
:deep(.fc-timegrid-slot-label[data-time^='12:']),
:deep(.fc-timegrid-slot-label[data-time^='13:']),
:deep(.fc-timegrid-slot-label[data-time^='17:']),
:deep(.fc-timegrid-slot[data-time^='12:']),
:deep(.fc-timegrid-axis[data-time^='12:']),
:deep(.fc-timegrid-slot[data-time^='13:']),
:deep(.fc-timegrid-axis[data-time^='13:']),
:deep(.fc-timegrid-slot[data-time^='17:2']),
:deep(.fc-timegrid-axis[data-time^='17:2']),
:deep(.fc-timegrid-slot[data-time^='17:4']),
:deep(.fc-timegrid-axis[data-time^='17:4']),
:deep(.fc-timegrid-axis[data-time^='18:0']),
:deep(.fc-timegrid-slot[data-time^='18:0']) {
  display: none !important;
}
</style>
